﻿using System.Collections.Generic;
using Mitosoft.Definitions.ExtendedBaseValues;
using TextFileReaderModule.Interfaces;

namespace TextFileReaderModule.Services
{
  /// <summary>
  /// Service retrieving and storing comments.
  /// </summary>
  public class CommentService : ICommentService
  {
	  #region fields

	  /// <summary>
    /// Contains all comments.
    /// </summary>
    protected readonly Dictionary<ulong, string> _allComments = new Dictionary<ulong, string>();

    /// <summary>
    /// Represents the dictionary containing all pairs where the key represents the source index
    /// and the value represents the comment ID.
    /// </summary>
    protected readonly Dictionary<int, HashSet<ulong>> _sourceIndexAndIDs = new Dictionary<int, HashSet<ulong>>();

	  /// <summary>
	  /// Represents <see cref="NeedsToBeSaved"/>
	  /// </summary>
	  protected IExtendedProperty<bool> _needsToBeSaved = new ExtendedProperty<bool>();

    #endregion

    #region constructor(s)

    /// <summary>
    /// Creates a new <see cref="CommentService"/> instance.
    /// </summary>
    // ReSharper disable once EmptyConstructor
    public CommentService()
    {
      // Intentionally left blank.
    }

    #endregion

    #region properties

	  /// <summary>
	  /// Gets whether the comments needs to be saved.
	  /// </summary>
	  public IExtendedProperty<bool> NeedsToBeSaved
	  {
		  get
		  {
			  return _needsToBeSaved;
		  }
		
		  set
		  {
			  _needsToBeSaved = value;
		  }
	  }

	  /// <summary>
	  /// Gets the available comments.
	  /// </summary>
	  protected Dictionary<ulong, string> AllComments
	  {
		  get
		  {
			  return _allComments;
		  }
	  }

	  #endregion

    #region methods

    /// <summary>
    /// Gets the comments.
    /// </summary>
    /// <param name="sourceIndex">Represents the source index.
    /// If null, all comments will be returned.</param>
    /// <param name="offset">Offset used to correct the id.</param>
    /// <returns>Found comments.</returns>
    public IEnumerable<KeyValuePair<ulong, string>> Comments(int? sourceIndex, ulong offset)
    {
      #region return all values

      if (sourceIndex == null)
      {
        foreach (KeyValuePair<ulong, string> IdCommentKVP in AllComments)
        {
          yield return IdCommentKVP;
        }

        yield break;
      }

      #endregion

      #region return all values belonging to the given source index

      foreach (var sourceIndexAndIDList in _sourceIndexAndIDs)
      {
        if (sourceIndexAndIDList.Key != sourceIndex) continue;

        foreach (ulong id in sourceIndexAndIDList.Value)
        {
          yield return new KeyValuePair<ulong, string>(id - offset, AllComments[id]);
        }
      }

      #endregion
    }

    /// <summary>
    /// Adds the given comment or replaces if the given id already exists.
    /// </summary>
    /// <param name="id">Represents the comment id.</param>
    /// <param name="comment">Represents the comment.</param>
    /// <param name="sourceIndex">Represents the source index.</param>
    public void Add(ulong id, string comment, int sourceIndex)
    {
      if (AllComments.ContainsKey(id) && AllComments[id] == comment)
        return; // comment already present.

      AllComments[id] = comment;
      
      if (!_sourceIndexAndIDs.ContainsKey(sourceIndex))
        _sourceIndexAndIDs.Add(sourceIndex, new HashSet<ulong>());
      
      _sourceIndexAndIDs[sourceIndex].Add(id);

      NeedsToBeSaved.Value = true;
    }

    /// <summary>
    /// Gets the comment being identified by the given id.
    /// </summary>
    /// <param name="id">Identifies the comment to be returned.</param>
    /// <returns>Found comment or null if the comment being identified by
    /// the given id does not exist.</returns>
    public string GetComment(ulong id)
    {
      return !AllComments.ContainsKey(id) ? null : AllComments[id];
    }

    /// <summary>
    /// Clears all comments.
    /// </summary>
    public void Clear()
    {
      AllComments.Clear();
	    NeedsToBeSaved.Value = false;
    }

    #endregion
  }
}
